﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ModeloMagiPack.Entidades;
using DALMagiPack.Funcionalidad;

namespace BLLMagiPack.Managers
{
    public class BLLMovimiento : BLLMagiPack
    {
        public void CargarNuevo(Movimiento movimiento,int? idCuentaBancaria, List<Cheque> cheques)
        {
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            dalMovimientos.IniciarConexión();
            dalMovimientos.IniciarTransacción();
            DALMagiPack<ChequeMovimiento> dalAsignaciones = new DALMagiPack<ChequeMovimiento>() { Connection = dalMovimientos.Connection, Transaction = dalMovimientos.Transaction };

            dalMovimientos.Insertar(movimiento);
            foreach (Cheque cheque in cheques)
            {
                ChequeMovimiento asignacion = new ChequeMovimiento();
                asignacion.IdCheque = cheque.IdCheque;
                asignacion.IdMovimiento = movimiento.IdMovimiento;
                dalAsignaciones.Insertar(asignacion);
            }

            if (idCuentaBancaria != null)
            {
                DALMagiPack<CuentaBancariaMovimiento> dalCuentaBancaria = new DALMagiPack<CuentaBancariaMovimiento>() { Connection = dalMovimientos.Connection, Transaction = dalMovimientos.Transaction };
                CuentaBancariaMovimiento asignacionBanco = new CuentaBancariaMovimiento();
                asignacionBanco.IdCuentaBancaria = (int)idCuentaBancaria;
                asignacionBanco.IdMovimiento = movimiento.IdMovimiento;
                dalCuentaBancaria.Insertar(asignacionBanco);
            }

            dalMovimientos.ConfirmarTransacción();
            dalMovimientos.CerrarConexión();
        }

        public void ModificarMovimiento(Movimiento movimiento, int? idCuentaBancaria, List<Cheque> cheques)
        {
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            dalMovimientos.IniciarConexión();
            dalMovimientos.IniciarTransacción();
            DALMagiPack<ChequeMovimiento> dalAsignaciones = new DALMagiPack<ChequeMovimiento>() { Connection = dalMovimientos.Connection, Transaction = dalMovimientos.Transaction };

            dalMovimientos.Modificar(movimiento);

            ChequeMovimiento asignacion = new ChequeMovimiento();
            List<Filtro> opciones = new List<Filtro>();
            List<ChequeMovimiento> asignaciones = dalAsignaciones.Traer(asignacion, opciones);
            foreach (ChequeMovimiento item in asignaciones)
                dalAsignaciones.Eliminar(item);

            foreach (Cheque cheque in cheques)
            {
                ChequeMovimiento asignacionNueva = new ChequeMovimiento();
                asignacionNueva.IdCheque = cheque.IdCheque;
                asignacionNueva.IdMovimiento = movimiento.IdMovimiento;
                dalAsignaciones.Insertar(asignacionNueva);
            }

            if (idCuentaBancaria != null)
            {
                DALMagiPack<CuentaBancariaMovimiento> dalCuentasBancariasMovimientos = new DALMagiPack<CuentaBancariaMovimiento>() { Connection = dalMovimientos.Connection, Transaction = dalMovimientos.Transaction };
                CuentaBancariaMovimiento asignacionBanco = new BLLCuentaBancariaMovimiento().TraerPorMovimiento(movimiento.IdMovimiento);
                if (asignacionBanco != null)
                    dalCuentasBancariasMovimientos.Eliminar(asignacionBanco);

                asignacionBanco = new CuentaBancariaMovimiento();
                asignacionBanco.IdCuentaBancaria = (int)idCuentaBancaria;
                asignacionBanco.IdMovimiento = movimiento.IdMovimiento;
                dalCuentasBancariasMovimientos.Insertar(asignacionBanco);
            }

            dalMovimientos.ConfirmarTransacción();
            dalMovimientos.CerrarConexión();
        }

        public void EliminarMovimiento(int idMovimiento)
        {
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            dalMovimientos.IniciarConexión();
            dalMovimientos.IniciarTransacción();
            DALMagiPack<ChequeMovimiento> dalAsignaciones = new DALMagiPack<ChequeMovimiento>() { Connection = dalMovimientos.Connection, Transaction = dalMovimientos.Transaction };

            ChequeMovimiento asignacion = new ChequeMovimiento();
            List<Filtro> opciones = new List<Filtro>();
            List<ChequeMovimiento> asignaciones = dalAsignaciones.Traer(asignacion, opciones);
            foreach (ChequeMovimiento item in asignaciones)
                dalAsignaciones.Eliminar(item);

            DALMagiPack<CuentaBancariaMovimiento> dalCuentaBancariaMovimiento = new DALMagiPack<CuentaBancariaMovimiento>() { Connection = dalMovimientos.Connection, Transaction = dalMovimientos.Transaction };
            CuentaBancariaMovimiento asignacionBanco = new BLLCuentaBancariaMovimiento().TraerPorMovimiento(idMovimiento);
            if (asignacionBanco != null)
                dalCuentaBancariaMovimiento.Eliminar(asignacionBanco);

            dalMovimientos.Eliminar(new Movimiento() { IdMovimiento = idMovimiento });

            dalMovimientos.ConfirmarTransacción();
            dalMovimientos.CerrarConexión();
        }

        public List<Movimiento> TraerMovimientos(DateTime? desde, DateTime? hasta, int? idCuentaBancaria)
        {
            Movimiento filtro = new Movimiento() { EsBanco = idCuentaBancaria != null };
            List<Filtro> opciones = new List<Filtro>() { new Filtro(){ Columna = NombresColumnas.EsBanco, Tipo = TipoFiltro.Equal} };
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            List<Movimiento> movimientos = dalMovimientos.Traer(filtro, opciones);
            
            List<Movimiento> filtrados = new List<Movimiento>();
            if (idCuentaBancaria == null)
                filtrados = movimientos;
            else
                filtrados = new BLLCuentaBancariaMovimiento().FiltrarPertenecientes(movimientos,(int) idCuentaBancaria);
            

            return (from m in filtrados
                    where (desde.HasValue && m.Fecha >= desde.Value.Date)
                    && (hasta.HasValue && m.Fecha <= hasta.Value.Date)
                    select m).ToList();
        }

        public List<Movimiento> TraerMovimientosPorAño(int año)
        {
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            List<Movimiento> movimientos = dalMovimientos.Traer(new Movimiento(), new List<Filtro>());

            return (from m in movimientos
                    where m.Fecha.Year == año
                    select m).ToList();
        }

        public Movimiento TraerMovimiento(int idMovimiento)
        {
            Movimiento filtro = new Movimiento() { IdMovimiento = idMovimiento };
            List<Filtro> opciones = new List<Filtro>() { new Filtro() { Columna = NombresColumnas.IdMovimiento, Tipo = TipoFiltro.Equal } };
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            List<Movimiento> movimientos = dalMovimientos.Traer(filtro, opciones);

            return movimientos.FirstOrDefault();
        }

        public List<Movimiento> TraerMovimientosPorCheque(int idCheque)
        {
            List<Movimiento> resulteitor = new List<Movimiento>();

            foreach (ChequeMovimiento item in new BLLChequeMovimiento().TraerAsignaciones(null, idCheque))
                resulteitor.Add(TraerMovimiento(item.IdMovimiento));
            
            return resulteitor;
        }

        public List<Movimiento> TraerMovimientosPorCheque(int idCheque, int mes)
        {
            List<Movimiento> resulteitor = new List<Movimiento>();

            foreach (ChequeMovimiento item in new BLLChequeMovimiento().TraerAsignaciones(null, idCheque))
            {
                Movimiento movimiento = TraerMovimiento(item.IdMovimiento);

                if(movimiento.Fecha.Month == mes)
                    resulteitor.Add(movimiento);
            }

            return resulteitor;
        }

        public bool PoseeMovimientos(int idCategoria)
        {
            Movimiento filtro = new Movimiento() { IdCategoria = idCategoria };
            List<Filtro> opciones = new List<Filtro>() { new Filtro() { Columna = NombresColumnas.IdCategoria, Tipo = TipoFiltro.Equal } };
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            int cantidad = dalMovimientos.Contar(filtro, opciones);
            return cantidad > 0;
        }

        #region Calculos

        /// <summary>
        /// Calcula el saldo de los movimientos de TODA la tabla. Puede llegar a ser muy lento.
        /// </summary>
        /// <param name="saldoPositivo"></param>
        /// <param name="saldoNegativo"></param>
        /// <param name="esBanco"></param>
        public void CalcularSaldos(out decimal saldoPositivo, out decimal saldoNegativo, int? idCuentaBancaria, int año, int mes, bool esFechaLimite)
        {
            saldoNegativo = saldoPositivo = 0;
            DALMagiPack<Movimiento> dal = new DALMagiPack<Movimiento>();

            Filtro filtroEsBanco = new Filtro() { Columna = NombresColumnas.EsBanco, Tipo = TipoFiltro.Equal };
            Filtro filtroEsIngreso = new Filtro() { Columna = NombresColumnas.EsIngreso, Tipo = TipoFiltro.Equal };
            List<Filtro> opciones = new List<Filtro>() { filtroEsBanco, filtroEsIngreso };

            List<Movimiento> negativos = dal.Traer(new Movimiento() { EsBanco = idCuentaBancaria != null, EsIngreso = false }, opciones).Where(m => m.Fecha <= DateTime.Now).ToList();
            List<Movimiento> positivos = dal.Traer(new Movimiento() { EsBanco = idCuentaBancaria != null, EsIngreso = true }, opciones).Where(m => m.Fecha <= DateTime.Now).ToList();

            if (idCuentaBancaria != null)
            {
                negativos = new BLLCuentaBancariaMovimiento().FiltrarPertenecientes(negativos, (int)idCuentaBancaria);
                positivos = new BLLCuentaBancariaMovimiento().FiltrarPertenecientes(positivos, (int)idCuentaBancaria);
            }

            if (esFechaLimite)
            {
                DateTime fechaLimite = new DateTime(año, mes, 1).AddMonths(1).Subtract(new TimeSpan(1, 0, 0, 0, 0));
                negativos = negativos.Where(m => m.Fecha <= fechaLimite).ToList();
                positivos = positivos.Where(m => m.Fecha <= fechaLimite).ToList();
            }
            else
            {
                negativos = negativos.Where(m => m.Fecha.Year == año && m.Fecha.Month == mes).ToList();
                positivos = positivos.Where(m => m.Fecha.Year == año && m.Fecha.Month == mes).ToList();
            }

            if (negativos.Count > 0)
                saldoNegativo = negativos.Sum(n => n.Monto);
            if (positivos.Count > 0)
                saldoPositivo = positivos.Sum(p => p.Monto);
        }

        public void CalcularGastosDeIVa(out decimal totalGastado, out decimal ivaCompra, DateTime fechaInicial, DateTime fechaFinal)
        {
            totalGastado = ivaCompra = 0;
            DALMagiPack<Movimiento> dalMovimientos = new DALMagiPack<Movimiento>();
            Movimiento filtro = new Movimiento() { IVADiscriminado = true };
            List<Movimiento> movimientos = dalMovimientos.Traer(filtro, new List<Filtro>() { new Filtro() { Columna = NombresColumnas.IVADiscriminado, Tipo = TipoFiltro.Equal } });
            List<Movimiento> filtrados = (from m in movimientos
                                          where m.Fecha >= fechaInicial
                                          && m.Fecha <= fechaFinal
                                          select m).ToList();

            foreach (Movimiento item in filtrados)
            {
                totalGastado += item.Monto;
                ivaCompra += item.PorcentajeIVA * item.Monto / 100;
            }

            BLLOrdenFacturaProveedor bllOrdenFactura = new BLLOrdenFacturaProveedor();
            foreach (FacturaCheque facturaCheque in new BLLFacturaCheque().Traer(fechaInicial, fechaFinal))
            {
                if (!bllOrdenFactura.EstaAsignadoProveedor(facturaCheque.IdCheque))
                    continue;

                Cheque cheque = new BLLCheque().TraerCheque(facturaCheque.IdCheque);
                totalGastado += cheque.Importe;

                Factura factura = new BLLFactura().TraerFactura(facturaCheque.IdFactura);
                ivaCompra += (decimal) factura.IVA * cheque.Importe / 100;
            }
        }

        public void CalcularGastosDeIVa(out decimal totalGastado, out decimal ivaCompra, Meses mes, int año)
        {
            DateTime inicial = new DateTime(año, (int)mes, 1);
            DateTime final = inicial.AddMonths(1).Subtract(new TimeSpan(1, 0, 0, 0));
            CalcularGastosDeIVa(out totalGastado, out ivaCompra, inicial, final);
        }

        #endregion
    }
}
